\defmodule {XYChart}

This class provides tools to create charts from data in a simple way. Its main
feature is to produce
 TikZ/PGF (see WWW link \url{http://sourceforge.net/projects/pgf/})
 compatible source code which can be included
in \LaTeX\ documents, but it can also produce charts in other formats.
One can easily create a new chart, and customize its appearance using methods
of this class, with the encapsulated
\externalclass{umontreal.iro.lecuyer.charts}{SSJXYSeriesCollection} object
representing the data, and the two
\externalclass{umontreal.iro.lecuyer.charts}{Axis} objects representing the axes.
All these classes depend on the \texttt{JFreeChart} API (see WWW link
\url{http://www.jfree.org/jfreechart/}) which provides tools to build charts with
Java, to draw them, and export them to files. However, only basic features are
used here.

Moreover, \texttt{XYChart} provides methods to plot data using a MATLAB friendly
syntax. None of these methods provides new features; they just propose a
different syntax to create charts. Therefore some features are unavailable
when using these methods only.

\bigskip\hrule
\begin{code}
\begin{hide}
/*
 * Class:        XYChart
 * Description:
 * Environment:  Java
 * Software:     SSJ
 * Copyright (C) 2001  Pierre L'Ecuyer and Universite de Montreal
 * Organization: DIRO, Universite de Montreal
 * @author
 * @since

 * SSJ is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License (GPL) as published by the
 * Free Software Foundation, either version 3 of the License, or
 * any later version.

 * SSJ is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * A copy of the GNU General Public License is available at
   <a href="http://www.gnu.org/licenses">GPL licence site</a>.
 */
\end{hide}
package umontreal.iro.lecuyer.charts;\begin{hide}
import java.io.*;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.annotations.*;
import org.jfree.chart.plot.XYPlot;
import org.jfree.ui.TextAnchor;
import javax.swing.JFrame;\end{hide}

public abstract class XYChart \begin{hide} {
   protected Axis XAxis;
   protected Axis YAxis;

   protected SSJXYSeriesCollection dataset;
   protected JFreeChart chart;
   protected boolean latexDocFlag = true;

   protected boolean autoRange;
   protected double[] manualRange;

   protected boolean grid = false;
   protected double xstepGrid;
   protected double ystepGrid;

   // this flag is set true when plotting probabilities. In that case,
   // y is always >= 0.
   protected boolean probFlag = false;

   protected double chartMargin = 0.02;   // margin around the chart
\end{hide}
\end{code}

%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsubsection*{Methods}

\begin{code}

   public JFreeChart getJFreeChart() \begin{hide} {
      return chart;
   }\end{hide}
\end{code}
\begin{tabb}
   Returns the \texttt{JFreeChart} object associated with this chart.
\end{tabb}
\begin{htmlonly}
   \return{the associated JFreeChart object.}
\end{htmlonly}
\begin{code}

   public Axis getXAxis() \begin{hide} {
      return XAxis;
   }\end{hide}
\end{code}
\begin{tabb}
   Returns the chart's domain axis ($x$-axis) object.
\end{tabb}
\begin{htmlonly}
   \return{chart's domain axis ($x$-axis) object.}
\end{htmlonly}
\begin{code}

   public Axis getYAxis() \begin{hide} {
      return YAxis;
   }\end{hide}
\end{code}
\begin{tabb}
   Returns the chart's range axis ($y$-axis) object.
\end{tabb}
\begin{htmlonly}
   \return{chart's range axis ($y$-axis) object.}
\end{htmlonly}
\begin{code}

   public abstract JFrame view (int width, int height);
\end{code}
\begin{code}

   public String getTitle() \begin{hide} {
      return chart.getTitle().getText();
   }\end{hide}
\end{code}
\begin{tabb}
   Gets the current chart title.
\end{tabb}
\begin{htmlonly}
   \return{Chart title.}
\end{htmlonly}
\begin{code}

   public void setTitle (String title) \begin{hide} {
      chart.setTitle(title);
   }\end{hide}
\end{code}
\begin{tabb}
   Sets a title to this chart. This title will appear on the chart displayed
 by method \method{view}{}.
\end{tabb}
\begin{htmlonly}
   \param{title}{chart title.}
\end{htmlonly}
\begin{code}

   public void setprobFlag (boolean flag) \begin{hide} {
      probFlag = flag;
   }\end{hide}
\end{code}
\begin{tabb}
   Must be set \texttt{true} when plotting probabilities,
  \texttt{false} otherwise.
\end{tabb}
\begin{htmlonly}
   \param{flag}{\texttt{true} for plotting probabilities}
\end{htmlonly}
\begin{code}

   public void setAutoRange() \begin{hide} {
      setAutoRange (false, false, true, true);
   }\end{hide}
\end{code}
\begin{tabb}
   The $x$ and the $y$ ranges of the chart are set automatically.
%   The axes cross at point $(0,0)$.
\end{tabb}
\begin{code}

   public void setAutoRange (boolean right, boolean top) \begin{hide} {
      setAutoRange (false, false, right, top);
   }\end{hide}
\end{code}
\begin{tabb}
   The $x$ and the $y$ ranges of the chart are set automatically.
 If \texttt{right} is \texttt{true}, the vertical axis will be on the left of
 the points, otherwise on the right. If \texttt{top} is \texttt{true},
 the horizontal axis  will be under the points, otherwise above the points.
\end{tabb}
\begin{htmlonly}
   \param{right}{true if the x-values on the right of axis.}
   \param{top}{true if the y-values on the top of axis.}
\end{htmlonly}
\begin{code}\begin{hide}
   private double[] adjustRangeBounds (double bmin, double bmax) {
      // resets chart lower and upper bounds to round values.
      // Returns corrected [lowerBound, upperBound]

      double del = (bmax - bmin)/20.0;    // Choose 20 intervals to round
      int a = (int) Math.floor(0.5 + Math.log10(del));
      double d = Math.pow(10.0, (double) a);     // power of 10
      double lower = d*Math.ceil((bmin - del)/d);
      if (lower > bmin)
         lower -= d;
      if (0 == Math.abs(bmin))
         lower = 0;
      double upper = d*Math.floor((bmax + del)/d);
      if (upper < bmax)
         upper += d;
      double [] range = new double[2];
      range[0] = lower;
      range[1] = upper;
      return range;
   }

   protected void setAutoRange (boolean xZero, boolean yZero, boolean right, boolean top) {
      // see description of setAxesZero
      autoRange = true;
      double BorneMin = (dataset.getDomainBounds())[0];
      double BorneMax = (dataset.getDomainBounds())[1];
      double del;
      if (BorneMax - BorneMin < 1)
         del = (BorneMax - BorneMin) * chartMargin;
      else
         del = chartMargin;
      if (BorneMin < 0.0) BorneMin *= 1.0 + del;
      else BorneMin *= 1.0 - del;
      if (BorneMax < 0.0) BorneMax *= 1.0 - del;
      else BorneMax *= 1.0 + del;
      double [] newRange = new double[2];
      newRange = adjustRangeBounds (BorneMin, BorneMax);
      if (probFlag && (BorneMin == 0.0))
         newRange[0] = 0.0;
      XAxis.getAxis().setLowerBound(newRange[0]);
      XAxis.getAxis().setUpperBound(newRange[1]);

      BorneMin = (dataset.getRangeBounds())[0];
      BorneMax = (dataset.getRangeBounds())[1];
      if (BorneMax - BorneMin < 1)
         del = (BorneMax - BorneMin) * chartMargin;
      else
         del = chartMargin;
      if (BorneMin < 0.0) BorneMin *= 1.0 + del;
      else BorneMin *= 1.0 - del;
      if (BorneMax < 0.0) BorneMax *= 1.0 - del;
      else BorneMax *= 1.0 + del;
      newRange = adjustRangeBounds (BorneMin, BorneMax);
      if (probFlag && (newRange[0] <= 0.0))   // probabilities are always >= 0
         newRange[0] = 0.0;
      YAxis.getAxis().setLowerBound(newRange[0]);
      YAxis.getAxis().setUpperBound(newRange[1]);

      if (xZero)
         XAxis.setTwinAxisPosition(0);
      else {
         if (right)
            XAxis.setTwinAxisPosition(XAxis.getAxis().getLowerBound());
         else
            XAxis.setTwinAxisPosition(XAxis.getAxis().getUpperBound());
      }

      if (yZero)
         YAxis.setTwinAxisPosition(0);
      else {
         if (top)
            YAxis.setTwinAxisPosition(YAxis.getAxis().getLowerBound());
         else
            YAxis.setTwinAxisPosition(YAxis.getAxis().getUpperBound());
      }
   }\end{hide}

   public void setAutoRange00 (boolean xZero, boolean yZero) \begin{hide} {
      setAutoRange (xZero, yZero, true, true);
   }\end{hide}
\end{code}
\begin{tabb}
 The $x$ and the $y$ ranges of the chart are set automatically.
If \texttt{xZero} is \texttt{true}, the vertical axis will pass through the
point $(0, y)$. If \texttt{yZero} is \texttt{true}, the horizontal axis
will pass through the point $(x, 0)$.
\end{tabb}
\begin{htmlonly}
  \param{xZero}{true if vertical axis passes through point 0}
  \param{yZero}{true if horizontal axis passes through point 0}
\end{htmlonly}
\begin{code}

   public void setManualRange (double[] range) \begin{hide} {
      setManualRange (range, false, false, true, true);
   }\end{hide}
\end{code}
\begin{tabb}
   Sets the $x$ and $y$ ranges of the chart  using the format: \texttt{range =
  [xmin, xmax, ymin, ymax]}. %The axes cross at point $(0,0)$.
\end{tabb}
\begin{htmlonly}
   \param{range}{new axis ranges.}
\end{htmlonly}
\begin{code}

   public void setManualRange (double[] range, boolean right, boolean top) \begin{hide} {
      setManualRange (range, false, false, right, top);
   }\end{hide}
\end{code}
\begin{tabb}
    Sets the $x$ and $y$ ranges of the chart  using the format: \texttt{range =
  [xmin, xmax, ymin, ymax]}.
  If \texttt{right} is \texttt{true}, the vertical axis will be on the left of
 the points, otherwise on the right. If \texttt{top} is \texttt{true},
 the horizontal axis  will be under the points, otherwise above the points.
\end{tabb}
\begin{htmlonly}
   \param{range}{new axis ranges.}
   \param{right}{true if the x-values on the right.}
   \param{top}{true if the y-values on the top.}
\end{htmlonly}
\begin{code}\begin{hide}

   private void setManualRange (double[] range, boolean xZero, boolean yZero,
                                boolean right, boolean top) {
      if (range.length != 4)
         throw new IllegalArgumentException (
             "range must have the format: [xmin, xmax, ymin, ymax]");
      autoRange = false;
      XAxis.getAxis().setLowerBound(Math.min(range[0],range[1]));
      XAxis.getAxis().setUpperBound(Math.max(range[0],range[1]));
      YAxis.getAxis().setLowerBound(Math.min(range[2],range[3]));
      YAxis.getAxis().setUpperBound(Math.max(range[2],range[3]));

      if (xZero)
         XAxis.setTwinAxisPosition(0);
      else {
         if (right)
            XAxis.setTwinAxisPosition(XAxis.getAxis().getLowerBound());
         else
            XAxis.setTwinAxisPosition(XAxis.getAxis().getUpperBound());
      }

      if (yZero)
         YAxis.setTwinAxisPosition(0);
      else {
         if (top)
            YAxis.setTwinAxisPosition(YAxis.getAxis().getLowerBound());
         else
            YAxis.setTwinAxisPosition(YAxis.getAxis().getUpperBound());
      }
   }\end{hide}

   public void setManualRange00 (double[] range, boolean xZero, boolean yZero) \begin{hide} {
      setManualRange (range, xZero, yZero, true, true);
   }\end{hide}
\end{code}
\begin{tabb}
  Sets the $x$ and $y$ ranges of the chart using the format: \texttt{range =
  [xmin, xmax, ymin, ymax]}.
 If \texttt{xZero} is \texttt{true}, the vertical axis will pass through the
point $(0, y)$. If \texttt{yZero} is \texttt{true}, the horizontal axis
will pass through the point $(x, 0)$.
\end{tabb}
\begin{htmlonly}
  \param{xZero}{true if vertical axis passes through point 0}
  \param{yZero}{true if horizontal axis passes through point 0}
\end{htmlonly}
\begin{code}

   public double getChartMargin() \begin{hide} {
      return chartMargin;
   }\end{hide}
\end{code}
\begin{tabb}
    Returns the chart margin, which is the fraction by which the chart
    is enlarged on its borders. The default value is $0.02$.
\end{tabb}
\begin{code}

   public void setChartMargin (double margin) \begin{hide} {
      if (margin < 0.0)
         throw new IllegalArgumentException ("margin < 0");
      chartMargin = margin;
   }\end{hide}
\end{code}
\begin{tabb}
    Sets the chart margin to \texttt{margin}. It is the fraction by
    which the chart is enlarged on its borders.
   Restriction: $\texttt{margin} \ge 0$.
\end{tabb}
\begin{htmlonly}
   \param{margin}{margin percentage amount.}
\end{htmlonly}
\begin{code}

   public abstract void setTicksSynchro (int s);
\end{code}
\begin{tabb}
   Synchronizes $x$-axis ticks to the $s$-th series $x$-values.
\end{tabb}
\begin{htmlonly}
   \param{s}{series.}
\end{htmlonly}
\begin{code}

   public void drawVerticalLine (double x, String name, double yfrac,
                                 boolean right) \begin{hide} {
      double ybottom = YAxis.getAxis().getLowerBound();
      final Object o = this;
      if (this instanceof HistogramChart)
         ybottom = 0;
      double ytop = YAxis.getAxis().getUpperBound();
      XYLineAnnotation line = new XYLineAnnotation(x, ybottom, x, ytop);
      XYTextAnnotation text = new XYTextAnnotation(name, x, ytop*yfrac);
      if (!right)
         text.setTextAnchor(TextAnchor.HALF_ASCENT_RIGHT);
      else
         text.setTextAnchor(TextAnchor.HALF_ASCENT_LEFT);
      XYPlot plot = getJFreeChart().getXYPlot();
      plot.addAnnotation(line);
      plot.addAnnotation(text);
   }\end{hide}
\end{code}
\begin{tabb}
Draws a vertical line on the chart at $x$-coordinate \texttt{x}.
\texttt{name} is written near the line at $y$ position \texttt{yfrac}
(a fraction of the $y$-size of the chart, 0 is the bottom, 1 is the top);
if \texttt{right} is \texttt{true}, \texttt{name} is written on the right
of the line, else on the left.
\end{tabb}
\begin{htmlonly}
   \param{x}{$x$-coordinate of the line}
   \param{name}{description of the line}
   \param{yfrac}{$y$-position of name}
   \param{right}{$x$-position of name}
\end{htmlonly}



%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsubsection*{Latex-specific methods}

\begin{code}

   public void enableGrid (double xstep, double ystep) \begin{hide} {
      this.grid = true;
      this.xstepGrid = xstep;
      this.ystepGrid = ystep;
   }\end{hide}
\end{code}
\begin{tabb}
   Puts a grid on the background. It is important to note that the grid is
   always shifted in such a way that it contains the axes. Thus, the grid does
   not always have an intersection at the corner points; this occurs
   only if the corner points are multiples of the steps: \texttt{xstep}
   and \texttt{ystep} sets the step in each direction.
\end{tabb}
\begin{htmlonly}
   \param{xstep}{sets the step in the x-direction.}
   \param{ystep}{sets the step in the y-direction.}
\end{htmlonly}
\begin{code}

   public void disableGrid() \begin{hide} {
      this.grid = false;
   }\end{hide}
\end{code}
\begin{tabb}
   Disables the background grid.
\end{tabb}
\begin{code}

   public abstract String toLatex (double width, double height);
\end{code}
\begin{tabb}
   Exports the chart to a \LaTeX\ source code using PGF/TikZ.
   This method constructs and returns a string that can be written to
   a \LaTeX\ document to render the plot. \texttt{width} and \texttt{height}
   represents the width and the height of the produced chart. These dimensions
   do not take into account the axes and labels extra space. The \texttt{width}
   and the \texttt{height} of the chart are measured in centimeters.
\end{tabb}
\begin{htmlonly}
   \param{width}{Chart's width in centimeters.}
   \param{height}{Chart's height in centimeters.}
   \return{LaTeX source code.}
\end{htmlonly}
\begin{code}

   public void toLatexFile (String fileName, double width, double height) \begin{hide} {
      String output = toLatex(width, height);
      Writer file = null;
      try {
         file = new FileWriter(fileName);
         file.write(output);
         file.close();
     } catch (IOException e) {
         System.err.println ("   toLatexFile:  cannot write to  " + fileName);
         e.printStackTrace();
         try {
            if (file != null)
               file.close();
         } catch (IOException ioe) {}
      }
  }\end{hide}
\end{code}
\begin{tabb}
   Transforms the chart to \LaTeX{} form and writes it in file \texttt{fileName}.
   The chart's width and height (in centimeters) are \texttt{width} and \texttt{height}.
\end{tabb}
\begin{code}

   public void setLatexDocFlag (boolean flag) \begin{hide} {
      latexDocFlag = flag;
   }\end{hide}
\end{code}
\begin{tabb}
Flag to remove the \verb#\documentclass# (and other) commands in the
created \LaTeX{} files.
If \texttt{flag} is \texttt{true},  then when charts are translated into
\LaTeX{} form, it will be as a self-contained file that can be directly
compiled with \LaTeX{}. However, in this form, the file cannot be included in
another \LaTeX{} file without causing compilation errors because of the multiple
instructions \verb#\documentclass# and \verb#\begin{document}#.
By setting \texttt{flag} to \texttt{false}, these instructions will be
removed from the \LaTeX{} chart files, which can then be included in a master
\LaTeX{} file. By default, the flag is \texttt{true}.
\end{tabb}
\begin{code}
\begin{hide}

   protected void setTick0Flags() {
      // Set flag true if first or last label is on perpendicular axis.
      // The label will be moved a little to the right (x-label), or above
      // (y-label) to prevent it from being on the perpendicular axis.
      // But it is unnecessary when graph begins or ends where label is;
      // in this case, flag is false.
      // We cannot put this method in Axis because it depends on the
      // other axis.
     double minAxis = Math.min (XAxis.getAxis().getRange().getLowerBound(),
                                                   XAxis.getTwinAxisPosition());
     double maxAxis = Math.max (XAxis.getAxis().getRange().getUpperBound(),
                                                     XAxis.getTwinAxisPosition());
     if (XAxis.getTwinAxisPosition() == minAxis ||
         XAxis.getTwinAxisPosition() == maxAxis)
        YAxis.setTick0Flag(false);
     else
        YAxis.setTick0Flag(true);

     minAxis = Math.min (YAxis.getAxis().getRange().getLowerBound(),
                                                   YAxis.getTwinAxisPosition());
     maxAxis = Math.max (YAxis.getAxis().getRange().getUpperBound(),
                                                     YAxis.getTwinAxisPosition());
     if (YAxis.getTwinAxisPosition() == minAxis ||
         YAxis.getTwinAxisPosition() == maxAxis)
        XAxis.setTick0Flag(false);
     else
        XAxis.setTick0Flag(true);
   }


   protected double computeXScale (double position) {
      double[] bounds = new double[2];
      bounds[0] = XAxis.getAxis().getLowerBound();
      bounds[1] = XAxis.getAxis().getUpperBound();

      if (position < bounds[0])
         bounds[0] = position;
      if (position > bounds[1])
         bounds[1] = position;
      bounds[0] -= position;
      bounds[1] -= position;
      return computeScale (bounds);
   }


   protected double computeYScale (double position) {
      double[] bounds = new double[2];
      bounds[0] = YAxis.getAxis().getLowerBound();
      bounds[1] = YAxis.getAxis().getUpperBound();

      if (position < bounds[0])
         bounds[0] = position;
      if (position > bounds[1])
         bounds[1] = position;
      bounds[0] -= position;
      bounds[1] -= position;
      return computeScale (bounds);
   }


   protected double computeScale (double[] bounds) {
      int tenPowerRatio = 0;
      // echelle < 1 si les valeurs sont grandes
      while (bounds[1] > 1000 || bounds[0] < -1000) {
         bounds[1] /= 10;
         bounds[0] /= 10;
         tenPowerRatio++;
      }
      // echelle > 1 si les valeurs sont petites
      while (bounds[1] < 100 && bounds[0] > -100) {
         bounds[1] *= 10;
         bounds[0] *= 10;
         tenPowerRatio--;
      }
      return 1/Math.pow(10, tenPowerRatio);
   }
}\end{hide}
\end{code}
